﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Windows;
using System.Windows.Threading;
using System.Xml.Linq;
using Microsoft.Win32;
using TallyJ1Reporter.Properties;
using TallyJ1Reporter.XmlSource;
using System.Linq;

namespace TallyJ1Reporter.ElectionRelated
{
	public class ElectionSource : DependencyObject, INotifyPropertyChanged
	{
		public static readonly DependencyProperty ElectionRootProperty =
			DependencyProperty.Register("ElectionRoot", typeof(XElement), typeof(ElectionSource));

		public static readonly DependencyProperty NumNamesProperty =
			DependencyProperty.Register("Ballots", typeof(int), typeof(ElectionSource));

		public static readonly DependencyProperty ApprovedForReportingProperty =
			DependencyProperty.Register("ApprovedForReporting", typeof(bool), typeof(ElectionSource));

		public static readonly DependencyProperty TitleProperty =
			DependencyProperty.Register("Title", typeof(string), typeof(ElectionSource));

		public static readonly DependencyProperty TotalEligibleProperty =
			DependencyProperty.Register("TotalEligible", typeof(int), typeof(ElectionSource));

		public static readonly DependencyProperty ParticipationProperty =
			DependencyProperty.Register("Participation", typeof(string), typeof(ElectionSource));

		public static readonly DependencyProperty ElectedProperty =
			DependencyProperty.Register("Elected", typeof(ObservableCollection<PersonCount>), typeof(ElectionSource));


		readonly ImportFromXml _electionImporter;
		FileSystemWatcher _fileSystemWatcher;
		DateTime _lastSourceChangedEventTime = DateTime.MinValue;

		public ElectionSource()
		{
			FileIsLoaded = false;

			_electionImporter = new ImportFromXml();

			LoadFromSource();
		}

		public int Ballots
		{
			get
			{
				return (int)GetValue(NumNamesProperty);
			}
			set
			{
				SetValue(NumNamesProperty, value);
			}
		}

		public int TotalEligible
		{
			get
			{
				return (int)GetValue(TotalEligibleProperty);
			}
			set
			{
				SetValue(TotalEligibleProperty, value);
			}
		}

		public string Participation
		{
			get
			{
				return (string)GetValue(ParticipationProperty);
			}
			set
			{
				SetValue(ParticipationProperty, value);
			}
		}


		public ObservableCollection<PersonCount> Elected
		{
			get
			{
				return (ObservableCollection<PersonCount>)GetValue(ElectedProperty);
			}
			set
			{
				SetValue(ElectedProperty, value);
			}
		}


		public string Title
		{
			get
			{
				return (string)GetValue(TitleProperty);
			}
			set
			{
				SetValue(TitleProperty, value);
			}
		}

		public bool ApprovedForReporting
		{
			get
			{
				return (bool)GetValue(ApprovedForReportingProperty);
			}
			set
			{
				SetValue(ApprovedForReportingProperty, value);
			}
		}

		public XElement ElectionRoot
		{
			get
			{
				return (XElement)GetValue(ElectionRootProperty);
			}
			set
			{
				SetValue(ElectionRootProperty, value);
			}
		}

		public string SourceFileName
		{
			get
			{
				return _electionImporter.FilePath;
			}
		}

		public bool FileIsLoaded
		{
			get;
			private set;
		}

		public List<string> ErrorMessages
		{
			get
			{
				return _electionImporter.ErrorMessages;
			}
		}

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		void LoadFromSource()
		{
			if (!AttachToFile(_electionImporter))
			{
				return;
			}

			LoadRawInfo();
			ExtractDetails();

			if (ElectionRoot == null)
			{
				return;
			}

			FileIsLoaded = true;
		}

		void ExtractDetails()
		{
			if (ElectionRoot == null)
				return;

			Title = ElectionRoot.Attribute("Name").Value("");

			var info = ElectionRoot.Element("Info");

			ApprovedForReporting = false;
			if (info != null)
			{
				ApprovedForReporting = info.Attribute("ApprovedForReporting").Value("false") == "true";
			}

			var final = ElectionRoot.Element("FinalCounts");
			if (final == null)
				return;

			TotalEligible = final.Attribute("AdultsInCommunity").Value("0").AsInt();
			var votedInPerson = final.Attribute("VotedInPerson").Value("0").AsInt();
			var mailedInBallots = final.Attribute("MailedInBallots").Value("0").AsInt();
			var droppedOffBallots = final.Attribute("DroppedOffBallots").Value("0").AsInt();

			Ballots = votedInPerson + mailedInBallots + droppedOffBallots;

			Participation = string.Format("{0} %", Math.Round(Ballots * 100.0 / TotalEligible));

			var elected = final.Elements()
				.Where(e => e.Attribute("Section").Value("") == "Top")
				.Select(e => new PersonCount(e));

			Elected = new ObservableCollection<PersonCount>(elected);
		}

		public void AskForNewSourceFile()
		{
			Settings.Default.ElectionFilePath = string.Empty;
			LoadFromSource();
		}

		bool AttachToFile(ImportFromXml electionImporter)
		{
			electionImporter.ErrorMessages.Clear();

			var fileName = Settings.Default.ElectionFilePath;
			_electionImporter.AttachTo(fileName);

			if (!_electionImporter.SourceExists)
			{
				electionImporter.ErrorMessages.Clear();

				var dialog = new OpenFileDialog
								{
									DefaultExt = ".xml",
									Filter = "Election Data File (.xml)|*.xml",
									CheckFileExists = true,
									Multiselect = false,
									Title = Resources.ApplicationTitle
								};

				var fileFound = dialog.ShowDialog();

				if (fileFound != true)
				{
					electionImporter.ErrorMessages.Add("No file selected.");
					electionImporter.RevertPathToActive();
					return false;
				}

				fileName = dialog.FileName;

				electionImporter.AttachTo(fileName);
			}

			if (electionImporter.ErrorMessages.Count != 0)
			{
				return false;
			}

			Settings.Default.ElectionFilePath = fileName;
			Settings.Default.Save();

			SetupFileWatcher(fileName);

			return true;
		}

		public event EventHandler<EventArgs> InfoReloaded;

		void SetupFileWatcher(string filePath)
		{
			try
			{
				if (_fileSystemWatcher != null)
				{
					_fileSystemWatcher.Dispose();
				}

				_fileSystemWatcher = new FileSystemWatcher(Path.GetDirectoryName(filePath),
														   Path.GetFileName(filePath))
										{
											NotifyFilter = NotifyFilters.LastWrite,
											EnableRaisingEvents = true
										};

				_fileSystemWatcher.Changed +=
					((sender, e) => Dispatcher.Invoke(DispatcherPriority.Normal, (Action)(delegate
																							{
																								_fileSystemWatcher.EnableRaisingEvents =
																									false;
																								SourceXmlWasChanged(e, _fileSystemWatcher);
																							})));
			}
			catch (Exception e)
			{
				MessageBox.Show(e.AllMessages());
			}
		}

		void SourceXmlWasChanged(FileSystemEventArgs e, FileSystemWatcher watcher)
		{
			// pause it for a bit...
			watcher.EnableRaisingEvents = false;
			Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, (Action)delegate
			{
				watcher.EnableRaisingEvents = true;
			});

			_lastSourceChangedEventTime = DateTime.Now;

			ReLoadElectionInfo();
		}

		void ReLoadElectionInfo()
		{
			//Debugger.Log(1, "Glen", "Reloading Names");

			LoadRawInfo();
			ExtractDetails();

			if (PropertyChanged != null)
			{
				PropertyChanged.Invoke(this, new PropertyChangedEventArgs("ElectionRoot"));
			}

			if (InfoReloaded != null)
			{
				InfoReloaded.Invoke(this, EventArgs.Empty);
			}
		}


		void LoadRawInfo()
		{
			_electionImporter.ImportData();

			if (!_electionImporter.ImportSuccessful)
			{
				return;
			}

			ElectionRoot = _electionImporter.ImportedElectionXml;
		}
	}
}